/*
 * A Gneral purpose java based Genome Library created by Susanta Tewari.
 * Copyright is protected. Contact Susanta Tewari at <statsusant@yahoo.com>
 */

package genomemap.tagging;

import genomemap.data.Clone;
import genomemap.data.ClonePool;
import genomemap.data.ComplementationData;
import genomemap.data.Gene;
import genomemap.data.HybridDataPool;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 *
 * @author Susanta Tewari <stewari@yahoo.com>
 */
public class HybridizationTaginStrategy extends TaggingStrategy {

    /** Field description */
    private final Map<String, Collection<String>> compl_data;

    /**
     * @concern This might lead to memory problems:
     *  Map<ChID,Map<CloneOrder,Clone>>
     */
    private final ClonePool clonePool;

    /**
     * hybridyzation data for all chromosomes: 1st dimension is for the chromosomes.
     * Note: @concern This might lead to memory problems
     * One possible solution is encapsulate the caching machanism used by creating a ClonePool class
     * that delivers the service and encapsulates the caching which is now localized and changed
     *  from system to system. Similary we can create HybridDataPool class that gives similar caching
     * encapsulation when working with all chromosome`s hybridization data. but all these call for a better
     * infrastructure than what we have now. This may be attempted after the first version.
     */
    private final HybridDataPool hybridDataPool;

    /**
     * Constructs ...
     *
     * @param complementationData description
     * @param clonePool description
     * @param hybridDataPool description
     */
    public HybridizationTaginStrategy(ComplementationData complementationData, ClonePool clonePool,
                                      HybridDataPool hybridDataPool) {

        this.compl_data     = complementationData.getGeneCloneTagData();
        this.clonePool      = clonePool;
        this.hybridDataPool = hybridDataPool;
    }

    @Override
    public void tag() {

        for (Clone clone : clones) {

            if (!clone.isTagged()) {

                for (Gene gene : genes) {

                    if (!gene.isTagged() && taggable(gene, clone)) {

                        gene.addTaggedClone(clone);
                        clone.addTaggedGene(gene);

                        break;
                    }
                }
            }
        }
    }

    /**
     * Method description
     *
     * @param gene description
     * @param clone description
     *
     * @return description
     */
    private boolean taggable(Gene gene, Clone clone) {

        if (!compl_data.keySet().contains(gene.getLabel())) return false;

        Collection<String> complCloneLabels = compl_data.get(gene.getLabel());


        // get connecting clones for clone in arg
        Collection<Clone> connClones = getConnectingClones(clone);

        for (Iterator<Clone> it = connClones.iterator(); it.hasNext(); ) {

            Clone connClone   = it.next();
            String cloneLabel = connClone.getLabel();

            for (Iterator<String> itstr = complCloneLabels.iterator(); itstr.hasNext(); ) {

                if (cloneLabel.equals(itstr.next())) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Method description
     *
     * @param clone description
     *
     * @return description
     */
    private Collection<Clone> getConnectingClones(Clone clone) {

        Collection<Clone> connectingClones  = new ArrayList<>();
        int ch_id                           = clone.getChId();
        int cloneOrder                      = clone.getOrder();
        List<Integer> connectingCloneOrders = new ArrayList<>();
        int[][] hybridData                  = hybridDataPool.getHybridData(ch_id);

        for (int i = 0; i < hybridData.length; i++) {

            if (hybridData[i][cloneOrder - 1] == 1) {
                connectingCloneOrders.add(new Integer(i + 1));
            }
        }

        for (Iterator<Integer> it = connectingCloneOrders.iterator(); it.hasNext(); ) {

            Integer order   = it.next();
            Clone connClone = clonePool.getClone(ch_id, cloneOrder);

            if (connClone != null) {
                connectingClones.add(connClone);
            }
        }

        return connectingClones;
    }
}
